<?php

/*

	[SupeV] (C)2007-2008 Comsenz Inc.
	This is NOT a freeware, use is subject to license terms
	$Id: cache.php 3304 2008-01-31 03:29:32Z xq $

*/
if (!isset($CACHE_INCLUDED))
{

	define('INFINITY_EXPIRED', 630720000); //无限缓存时间 用20年代替
	define('MAX_FILE_TIME', 2592000); //默认删除30天没用过的缓存文件

	$timestamp = time();

	/**
	 * validate and get container of cache
	 *
	 * @param string $cache_container
	 * @return bool
	 */

	function _cache_container_validate($cache_type = 'other')
	{
		global $type_arr;
		if(!_cache_type_validate($cache_type))
		{
			return false;
		}
		$cache_container = $type_arr[$cache_type];
		$container_arr = array('file', 'db', 'ea');	//目前实现了三种缓存容器，如果有兴趣，您可以自行添加其他容器
		if(in_array($cache_container, $container_arr))
		{
			return $cache_container;
		}
		else
		{
			return 'file';
		}
	}

	/**
	 * validate type of cache
	 *
	 * @param string $cache_type
	 * @return bool
	 */

	function _cache_type_validate($cache_type = 'other')
	{
		global $type_arr; //添加缓存类型时,在config.php文件中更新此数组
		return (array_key_exists($cache_type, $type_arr));
	}


	/**
	 * Returns the filename for the specified key.
	 *
	 * @param string  $key
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 * @return   string  full filename with the path
	 */
	function _get_filename($key, $cache_type = 'other', $depth = true)
	{
		if(strlen($key) < 3 || !_cache_type_validate($cache_type))
		{
			return false;
		}

		$tmp_dir = CACHE_PATH.$cache_type;
		if (!file_exists($tmp_dir))
		{
			@mkdir($tmp_dir, 0755);
		}
		if($depth)
		{
			$path_a = ord(substr($key, -1, 1));
			$path_b = ord(substr($key, -2, 1));

			$tmp_dir .= '/'.$path_a;
			if (!file_exists($tmp_dir))
			{
				@mkdir($tmp_dir, 0755);
			}
			$tmp_dir .= '/'.$path_b;
			if (!file_exists($tmp_dir))
			{
				@mkdir($tmp_dir, 0755);
			}

		}
		return $tmp_dir.'/'.$key.'.php';
	} // end func getFilename


	/**
	 * read cache
	 *
	 * @param string $key
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 * @return if cache exists return cache array, else return array()
	 */
	function readcache($key, $cache_type = 'other', $depth = true)
	{
		if($cache_container = _cache_container_validate($cache_type))
		{
			$function_name = '_readcache'.$cache_container;
			return call_user_func($function_name, $key, $cache_type, $depth);
		}
	}

	/**
	 * write cache
	 *
	 * @param string $key
	 * @param string $data
	 * @param int $expired expired time -1代表缓存失效 0代表无限时间缓存 甚用
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 * @return bool
	 */
	function writecache($key, $data, $expired = -1, $cache_type = 'other', $depth = true)
	{
		if($cache_container = _cache_container_validate($cache_type))
		{
			$function_name = '_writecache'.$cache_container;
			$writecache_res =  call_user_func($function_name, $key, $data, $expired, $cache_type, $depth);
			return $writecache_res;

		}
	}

	/**
	 * remove cache
	 * @param string $key
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 *
	 */
	function remove($key, $cache_type = 'other', $depth = true)
	{
		if($cache_container = _cache_container_validate($cache_type))
		{
			$function_name = '_remove'.$cache_container;
			return call_user_func($function_name, $key, $cache_type, $depth);
		}
	} // end func remove

	/**
	 * 批量删除过期或者超时缓存
	 *
	 * @param int $maxlifetime 缓存生命时间
	 * @param sting $cache_type
	 * @return bool
	 */
	function garbagecollection($maxlifetime = MAX_FILE_TIME, $cache_type = 'other')
	{
		if($cache_container = _cache_container_validate($cache_type))
		{
			$function_name = '_garbagecollection'.$cache_container;
			return call_user_func($function_name, $maxlifetime, $cache_type);
		}
	} // end func garbagecollection

	/**
	 * 全面更新缓存
	 *
	 * @param sting $cache_type
	 * @return bool
	 */
	function cache_flush($cache_type = 'other')
	{
		if($cache_container = _cache_container_validate($cache_type))
		{
			$function_name = '_cache_flush'.$cache_container;
			return call_user_func($function_name, $cache_type);
		}
	}

	/**
	 * flush cache from db
	 *
	 * @param sting $cache_type
	 * @return bool
	 */
	function _cache_flushdb($cache_type = 'other')
	{
		global $db, $tablepre;
		$sql = "delete from {$tablepre}caches where type='$cache_type'";
		mysql_query($sql, $db);
		return true;
	}

	/**
	 * flush cache from file
	 *
	 * @param sting $cache_type
	 * @return bool
	 */
	function _cache_flushfile($cache_type = 'other')
	{
		$return = _cache_deletedir(CACHE_PATH.$cache_type.'/');
		clearstatcache();
		return $return;
	}

	/**
	 * flush cache from eaccelerator //只有在共享内存足够的时候用
	 *
	 * @param sting $cache_type
	 * @return bool
	 */
	function _cache_flushea($cache_type = 'other')
	{
		$eaccelerator_keys = eaccelerator_list_keys();
		foreach($eaccelerator_keys as $k => $v)
		{
			if(!empty($cache_type) && 0 === strpos($v['name'], $cache_type))
			{
				eaccelerator_rm($v['name']);
			}
			elseif(empty($cache_type))
			{
				eaccelerator_rm($v['name']);
			}
		}
		return true;
	}

	/**
	* Deletes a directory and all files in it.
	*
	* @param string  directory
	* @return bool
	*/
	function _cache_deletedir($dir)
	{
		if (!is_writable($dir) || !is_readable($dir) || !($dh = opendir($dir)))
		{
			return false;
		}
		while (false !== $file = readdir($dh))
		{
			if ('.' == $file || '..' == $file || '.svn' == $file)
			{
				continue;
			}

			$file = $dir . $file;
			if (is_dir($file))
			{
				$file .= '/';
				$num = _cache_deletedir($file . '/');
			}
			else
			{
				@unlink($file);
			}
	        }
	        closedir($dh);
	        unset( $dh);
		if ($dir != CACHE_PATH)
		{  //delete the sub-dir entries  itself also, but not the cache-dir.
			@rmdir($dir);
		}
		return true;
	} // end func deleteDir

	/**
	 * read cache from db
	 *
	 * @param string $key
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 * @return if cache exists return cache array, else return array()
	 */
	function _readcachedb($key, $cache_type = 'other', $depth = true)
	{
		global $db, $tablepre, $timestamp;
		$sql = "SELECT * FROM {$tablepre}caches WHERE keyname='$key' and type='$cache_type'";
		$res = mysql_query($sql, $db);
		if($row = mysql_fetch_array($res))
		{
			if($row['expired'] > 0 && $row['expired'] < $timestamp)
			{
				return -1;
			}
			return (unserialize($row['data']));
		}
		else
		{
			return -1;
		}
	}

	/**
	 * read cache from eaccelerator
	 *
	 * @param string $key
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 * @return if cache exists return cache array, else return array()
	 */
	function _readcacheea($key, $cache_type = 'other', $depth = true)
	{

		if($data = eaccelerator_get($cache_type.$key))
		{
			return $data;
		}
		else
		{
			return -1;
		}
	}

	/**
	 * write to cache in db
	 *
	 * @param string $key maked md5
	 * @param string $data
	 * @param int $expired expired time -1代表缓存失效 0代表无限时间缓存 慎用
	 * @param sting $cache_type
	 * @param bool $depth 是否使用深度分层文件夹
	 */
	function _writecachedb($key, $data, $expired = -1, $cache_type = 'other', $depth = true)
	{
		global $db, $tablepre, $timestamp;
		$expired = ($expired == 0) ? INFINITY_EXPIRED : $expired;
		$sql = "REPLACE INTO {$tablepre}caches (
				keyname, ctime, expired, data, type
	       		) VALUES (
				'$key', '".$timestamp."', '".($expired + $timestamp)."', '".(_addcslashes_deep(serialize($data)))."', '".$cache_type."'
			)";
		mysql_query($sql, $db);
		return true;
	}

	/**
	 * read cache from file
	 *
	 * @param string $key
	 * @return if caches exists return this, else return array().
	 */
	function _readcachefile($key, $cache_type = 'other', $depth = true)
	{
		global $timestamp;
		$file = _get_filename($key, $cache_type, $depth);
		if(@include($file))
		{
			if($expired > 0 && $expired < $timestamp)
			{
				return -1;
			}
			touch($file, $timestamp);
			return ($data);
		}
		else
		{
			return -1;
		}
	}

	/**
	 * write to cache in file
	 *
	 * @param string $key maked md5().
	 * @param string $data cache's data.
	 * @param int $expired expired time. -1代表缓存失效 0代表无限时间缓存 甚用
	 */
	function _writecachefile($key, $data, $expired = -1, $cache_type = 'other', $depth = true)
	{
		global $timestamp;
		$expired = ($expired == 0)? INFINITY_EXPIRED: $expired;
		$fp = fopen(_get_filename($key, $cache_type, $depth), 'wb');
		if (!($fp))
		{
		    return false;
		}
		flock($fp, LOCK_EX);
		fwrite($fp, "<?php\n\$expired = ".($expired + $timestamp).";\n\$data = ".(_arrayeval($data)).";\n?>");
		//	flock($fp, LOCK_UN);
		fclose($fp);
		return true;
	}


	/**
	 * write to cache in eaccelerator
	 *
	 * @param string $key maked md5().
	 * @param string $data cache's data.
	 * @param int $expired expired time. -1代表缓存失效 0代表无限时间缓存 甚用
	 */
	function _writecacheea($key, $data, $expired = -1, $cache_type = 'other', $depth = true)
	{

		$expired = ($expired == 0)? INFINITY_EXPIRED: $expired;
		return eaccelerator_put($cache_type.$key, $data, $expired);

	}

	/**
	 * removedb
	 *
	 * @param string $key
	 * @return bool
	 */
	function _removedb($key, $cache_type = 'other', $depth = true)
	{
		global $db, $tablepre;
		$sql = "delete FROM {$tablepre}caches WHERE `keyname`='$key' and `type`='$cache_type'";
		mysql_query($sql, $db);
		return true;
	}


	/**
	 * removefile
	 *
	 * @param string $key
	 * @return bool
	 */
	function _removefile($key, $cache_type = 'other', $depth = true)
	{
		$file = _get_filename($key, $cache_type, $depth);
		if (file_exists($file))
		{
			$ok = @unlink($file);
			clearstatcache();
			return $ok;
		}
		return false;
	} // end func remove


	/**
	 * removefile
	 *
	 * @param string $key
	 * @return bool
	 */
	function _removeea($key, $cache_type = 'other', $depth = true)
	{
		return eaccelerator_rm($cache_type.$key);

	} // end func remove

	/**
	 * 删除缓存表中过期的数据
	 *
	 * @param int $maxlifetime
	 * @return bool
	 */
	function _garbagecollectiondb($maxlifetime, $cache_type = 'other')
	{
		global $db, $tablepre, $timestamp;
		$sql = "delete FROM {$tablepre}caches WHERE type='$cache_type' and expired <= '".$timestamp."' ";
		mysql_query($sql, $db);
		return true;
	}

	/**
	 * 删除缓存表中过期的数据 eaccelerator
	 *
	 * @param int $maxlifetime
	 * @return bool
	 */
	function _garbagecollectionea($maxlifetime, $cache_type = 'other')
	{
		eaccelerator_gc();
		return true;
	}

	/**
	 * 删除缓长期不用的文件缓存
	 *
	 * @param int $maxlifetime
	 *
	 */
	function _garbagecollectionfile($maxlifetime, $cache_type = 'other')
	{
		return _dogarbagecollectionfile($maxlifetime, CACHE_PATH.$cache_type.'/');
		clearstatcache();
	} // end func garbagecollectionfile

	function _dogarbagecollectionfile($maxlifetime, $dir)
	{
		global $timestamp;
		if (!is_writable($dir) || !is_readable($dir) || !($dh = opendir($dir)))
		{
			return false;
		}

		while ($file = readdir($dh))
		{
			if ('.' == $file || '..' == $file || '.svn' == $file)
			{
				continue;
			}

			$file = $dir . $file;
			if (is_dir($file))
			{
				_dogarbagecollectionfile($maxlifetime, $file . '/');
				continue;
			}

			// skip trouble makers but inform the user
			if (!file_exists($file))
			{
				continue;
			}
			$lastused_time = filemtime($file);
			// remove
			if ($lastused_time <= ($timestamp - $maxlifetime))
			{
				@unlink($file);
			}
		}
		closedir($dh);
	} // end func _dogarbagecollectionfile



	/**
	 * array addcslashes string.
	 *
	 * @param array $value
	 * @return addcslashes string from array.
	 */

	function _addcslashes_deep($value)
	{
		$value = is_array($value) ?
			array_map('_addcslashes_deep', $value) :
			addcslashes($value, '\'\\');

		return $value;

	}


	/**
	 * array format string.
	 *
	 * @param array $array
	 * @param int $level
	 * @return format string from array.
	 */

	function _arrayeval($array, $level = 0)
	{

		if(!is_array($array))
		{
			return "'".$array."'";
		}
		//note use defined function to export array
		if(is_array($array) && function_exists('var_export'))
		{
			return var_export($array, true);
		}

		$space = '';
		for($i = 0; $i <= $level; $i++)
		{
			$space .= "\t";
		}
		$evaluate = "Array\n$space(\n";
		$comma = $space;
		if(is_array($array))
		{
			foreach($array as $key => $val)
			{
				$key = is_string($key) ? '\''.addcslashes($key, '\'\\').'\'' : $key;
				$val = !is_array($val) && (!preg_match("/^\-?[1-9]\d*$/", $val) || strlen($val) > 12) ? '\''.addcslashes($val, '\'\\').'\'' : $val;
				if(is_array($val))
				{
					$evaluate .= "$comma$key => "._arrayeval($val, $level + 1);
				}
				else
				{
					$evaluate .= "$comma$key => $val";
				}
				$comma = ",\n$space";
			}
		}
		$evaluate .= "\n$space)";
		return $evaluate;
	}


	$CACHE_INCLUDED = TRUE;
}

?>